package cpabe

// package datashare

import (
	"bufio"
	"crypto/sha256"
	"database/sql"
	_ "encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	//"medical/cpabe"
	"os"
	"strconv"
	"strings"
	"time"

	_ "github.com/go-sql-driver/mysql" //初始化
	_ "hash/fnv"
	_ "math/rand"
)

//// db ACTION
// Db数据库连接池
var DB *sql.DB
var currentcode string

// 数据库配置
const (
	userName = "root"
	password = "root"
	ip       = "localhost"
	port     = "3306"
	dbName   = "itbtsql"
)

// 数据库初始化，原始文件里有
func InitDB() *sql.DB {
	//构建连接："用户名:密码@tcp(IP:端口)/数据库?charset=utf8"
	path := userName + ":" + password + "@tcp(" + ip + ":" + port + ")/" + dbName + "?allowNativePasswords=true"
	fmt.Println(path)
	//打开数据库,前者是驱动名，所以要导入： _ "github.com/go-sql-driver/mysql"
	DB, _ = sql.Open("mysql", path)
	//设置数据库最大连接数
	DB.SetConnMaxLifetime(100)
	//设置上数据库最大闲置连接数
	DB.SetMaxIdleConns(10)
	//验证连接
	if err := DB.Ping(); err != nil {
		fmt.Println("open database fail")
		fmt.Println(err)
	}
	fmt.Println("connnect success")
	return DB
}

// 查询并返回数据，数据库里有
func AdvanceQuery(DB *sql.DB, SQL string) map[int]string { //通用查询
	rows, err := DB.Query(SQL) //执行SQL语句，比如select * from users
	if err != nil {
		panic(err)
	}
	columns, _ := rows.Columns()            //获取列的信息
	count := len(columns)                   //列的数量
	var values = make([]interface{}, count) //创建一个与列的数量相当的空接口
	for i, _ := range values {
		var ii interface{} //为空接口分配内存
		values[i] = &ii    //取得这些内存的指针，因后继的Scan函数只接受指针
	}
	index := 0
	data := make(map[int]string) //用于存放1列的 [键/值] 对
	for rows.Next() {
		err := rows.Scan(values...) //开始读行，Scan函数只接受指针变量

		if err != nil {
			panic(err)
		}
		// colName 为列字段
		// for i, colName := range columns
		for i, _ := range columns {
			var raw_value = *(values[i].(*interface{})) //读出raw数据，类型为byte
			b, _ := raw_value.([]byte)
			v := string(b)  //将raw数据转换成字符串
			data[index] = v //colName是键，v是值
			index = index + 1
		}
	}

	defer rows.Close()

	if len(data) != 0 {
		return data
	}
	return nil
}

//// DB ACTION END

// FIRST Write
// 根据CaseNumber将整条数据写入txt文件中，并生成base64【未用】
func CaseNumberData(DB *sql.DB, casenumber string) {

	SQLString := "select * from base_info where _CaseNumber='" + casenumber + "'"
	rows, _ := DB.Query(SQLString)
	var _SubjectMark string
	var _Researcher string
	var _Organization string
	var _Diseases string
	var _CaseNumber string
	var _GatherTime string
	var _Groups string
	var _Name string
	var _NameInitials string
	var _Sex string
	var _Nation string
	var _MedicalHistory string
	var _NativePlace string
	var _Diagnose string
	var _Addition1 string
	var _Addition2 string
	var _Addition3 string
	var _Status string
	var _EntryTime string
	var _BaseTime string
	// index := 0

	for rows.Next() {
		rows.Scan(&_SubjectMark,
			&_Researcher,
			&_Organization,
			&_Diseases,
			&_CaseNumber,
			&_GatherTime,
			&_Groups,
			&_Name,
			&_NameInitials,
			&_Sex,
			&_Nation,
			&_MedicalHistory,
			&_NativePlace,
			&_Diagnose,
			&_Addition1,
			&_Addition2,
			&_Addition3,
			&_Status,
			&_EntryTime,
			&_BaseTime)
		data := [...]string{_SubjectMark, _Researcher, _Organization, _Diseases, _CaseNumber, _GatherTime, _Groups, _Name, _NameInitials, _Sex, _Nation, _MedicalHistory, _NativePlace, _Diagnose, _Addition1, _Addition2, _Addition3, _Status, _EntryTime, _BaseTime}
		jsonStr, _ := json.Marshal(data) // 转化为json格式
		//fmt.Println(string(jsonStr))
		WriteIntoTxtOver(casenumber, string(jsonStr))
		// read_data, _ := ReadBybuffio_1("./cpabe/dir_data/" + casenumber + ".txt") // 读取文件中的内容
		// //fmt.Println("read_data:",read_data)
		// res := base64.StdEncoding.EncodeToString([]byte(read_data[0]))
		// fmt.Println(res)
		// s, err := base64.StdEncoding.DecodeString(res) // base64解码
		// fmt.Println(string(s), err)

		// index = index + 1
	}

}

// 读取文件
func ReadBybuffio_1(path string) (map[int]string, bool) {
	linelist := make(map[int]string)
	file, err := os.Open(path)
	if err != nil {
		fmt.Printf("打开文件失败,err:%v\n", err)
		return linelist, false
	}
	defer file.Close() //关闭文件,为了避免文件泄露和忘记写关闭文件

	//使用buffio读取文件内容
	reader := bufio.NewReader(file) //创建新的读的对象

	index := 0
	for {
		line, _ := reader.ReadString('\n')
		if line == "" {
			break
		}
		//fmt.Println(line)
		linelist[index] = line
		index += 1
	}
	return linelist, true
}

// 追加方式写入basehash
func WriteIntoTxt(name string, data string) {
	file, err := os.OpenFile("basehash/"+name+".txt", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0777)
	defer file.Close()
	file.WriteString(data + "\n")
	if err != nil {
		fmt.Println(err)
	}
}

func GetAllFile(pathname string, s []string) ([]string, error) {
	rd, err := ioutil.ReadDir(pathname)
	fmt.Println(rd)
	if err != nil {
		fmt.Println("read dir fail:", err)
		return s, err
	}

	for _, fi := range rd {
		if !fi.IsDir() {
			fullName := pathname + "/" + fi.Name()
			fmt.Println(fullName)
			s = append(s, fullName)
		}
	}
	return s, nil
}

// 覆盖方式写入dir_data
func WriteIntoTxtOver(name string, data string) {
	fmt.Println("start get " + name + " 's info into txt")
	//var s []string
	//s, _ = GetAllFile(".", s)
	file, err := os.OpenFile("./cpabe/dir_data/"+name+".txt", os.O_WRONLY|os.O_CREATE, 0644)
	defer file.Close()
	file.WriteString(data)
	if err != nil {
		fmt.Println(err)
	}
}

// 针对数据所有者设置数据共享时长
func DataSharewithTime(DB *sql.DB, caseNumber string, chooseTime string) bool {

	// 获取年月日
	place1 := strings.Index(chooseTime, ",")
	place2 := strings.LastIndex(chooseTime, ",")
	year := chooseTime[:place1]
	month := chooseTime[place1+1 : place2]
	day := chooseTime[place2+1:]

	// 年月日转为int
	year1, _ := strconv.Atoi(year)
	month1, _ := strconv.Atoi(month)
	day1, _ := strconv.Atoi(day)
	//fmt.Println(year1, month1, day1)

	// 从数据库中读出初始时间
	var gather_time string
	SqlString1 := "select _GatherTime from base_info where _CaseNumber='" + caseNumber + "'"
	//fmt.Println(SqlString1)
	rows, _ := DB.Query(SqlString1)
	for rows.Next() {
		rows.Scan(&gather_time)
		//fmt.Println(gather_time)
	}

	tt, _ := time.ParseInLocation("2006-01-02 15:04:05", gather_time, time.Local)
	ttemp := tt.AddDate(+1, 0, 0)                                       // 加一年
	endtime := time.Unix(ttemp.Unix(), 0).Format("2006-01-02 15:04:05") // 数据有效期最终时间
	// 未在当前时间做共享操作-???
	// 获取当前时间
	data_share := time.Now().AddDate(+year1, +month1, +day1) // 从初始时间添加对应时间 //共享时长
	share_endtime := time.Unix(data_share.Unix(), 0).Format("2006-01-02 15:04:05")

	if share_endtime <= endtime {
		SQLString2 := "UPDATE base_info SET _BaseTime='" + share_endtime + "' where _CaseNumber='" + caseNumber + "'"
		_, err := DB.Exec(SQLString2)
		if err != nil {
			fmt.Println(err)
			return false
		}

		fmt.Println("可以共享")
	} else {
		// share_endtime 以数据有效期 最终时间为准 endtime
		fmt.Println("共享时间不在数据有效访问时间段内")
	}
	return share_endtime < endtime
}

func GetEncfileDir2SQL(DB *sql.DB, user string, casenumber string, policy string) string {
	fmt.Println("start GetEncfileDir2SQL")
	CaseNumberData(DB, casenumber)
	// 2 get enc path
	file_cpabe_Path := CpabeMake(user, casenumber, policy)

	rand.NewSource(time.Now().UnixNano())                                                           //使用当前的纳秒生成一个随机源，也就是随机种子
	randnum := fmt.Sprintf("%04v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000)) //生成一个rand
	// 将加密后路径存入数据库Addition1中
	SQLString := "UPDATE base_info SET _Addition1= '" + file_cpabe_Path + "', _Addition2='" + string(randnum) + "' where _CaseNumber='" + casenumber + "'"
	_, err := DB.Exec(SQLString)
	if err != nil {
		fmt.Println(err)
	}
	return file_cpabe_Path
}

// 生成共享值并将每次生成的hash存入文件中
func CreateShare(DB *sql.DB, casenumber string) string {

	var base_time string
	SqlString1 := "select _BaseTime from base_info where _CaseNumber='" + casenumber + "'"
	//fmt.Println(SqlString1)
	rows, _ := DB.Query(SqlString1)
	for rows.Next() {
		rows.Scan(&base_time)
		//fmt.Println(base_time)
	}
	// tt, _ := time.ParseInLocation("2006-01-02 15:04:05", base_time, time.Local)
	// endtime := time.Unix(tt.Unix(), 0).Format("2006-01-02 15:04:05") // 数据有效期最终时间

	// 获取当前时间
	data_share := time.Now()
	// share_endtime := time.Unix(data_share.Unix(), 0).Format("2006-01-02 15:04:05")

	// if share_endtime > endtime || share_endtime == endtime {
	// 	fmt.Println("数据有效期已过，无法共享")
	// 	//err := os.Remove("./cpabe/basehash/" + casenumber + ".txt")
	// 	//if err != nil {
	// 	//	fmt.Println(err)
	// 	//	return ""
	// 	//}
	// 	return ""
	// }

	timestamp := data_share.UnixNano()
	//fmt.Println(timestamp)
	var random string
	SQLString := "select _Addition2 from base_info where _CaseNumber='" + casenumber + "'"
	rows1, _ := DB.Query(SQLString)
	for rows1.Next() {
		rows1.Scan(&random)
		//fmt.Println(random)
	}

	// 取商
	random_num, _ := strconv.Atoi(random)
	quotient := timestamp / int64(random_num)
	fmt.Println(quotient)
	sharecode := strconv.FormatInt(quotient, 10)
	sharecode = sharecode[len(sharecode)-6:] // 取后六位
	currentcode = sharecode
	//fmt.Println(sharecode)
	basehash := fmt.Sprintf("%x", sha256.Sum256([]byte(sharecode))) // 生成hash值
	//fmt.Println(basehash)
	WriteIntoTxt(casenumber, basehash) // 写入basehash文件夹中
	SQLString1 := "UPDATE base_info SET _Addition2='" + sharecode + "' where _CaseNumber='" + casenumber + "'"
	_, err := DB.Exec(SQLString1)
	if err != nil {
		fmt.Println(err)
	}
	return sharecode
}

// 根据输入的代码判断是否能共享
func CertShare(casenumber string, code string) (string, bool) {
	datalist, flag := ReadBybuffio_1("./cpabe/basehash/" + casenumber + ".txt") // 读取对应文件
	if flag == false {
		return "", false
	}
	certhash := fmt.Sprintf("%x", sha256.Sum256([]byte(code)))
	for i := range datalist {
		data := strings.Replace(datalist[i], "\n", "", -1)
		if data == certhash {
			fmt.Println("该用户可以访问共享数据")
			var datapath string
			DB := InitDB()
			SQLString := "select _Addition1 from base_info where _CaseNumber='" + casenumber + "'"
			rows1, _ := DB.Query(SQLString)
			for rows1.Next() {
				rows1.Scan(&datapath)
			}
			return datapath, true
		}
	}
	fmt.Println("该用户不可以访问共享数据")
	return "", false
}

// 生成策略
func GetPolicy(DB *sql.DB, username string) string {
	var usertype string
	SQLString := "select usertype from login where username='" + username + "'"
	rows1, _ := DB.Query(SQLString)
	for rows1.Next() {
		rows1.Scan(&usertype)
	}
	SQLString1 := "select user_role,user_insti,user_disease from user_type where user_id='" + usertype + "'"
	data := AdvanceQuery(DB, SQLString1)
	policy := "((" + data[1] + " AND " + data[2] + ") OR " + data[0] + " )"
	fmt.Println(policy)
	return policy

}

// 函数：casenumb、共享时间，访问策略-修改
func DataEnShare(DB *sql.DB, casenumber string, policy string, time string) (bool, string, string) {

	flag := DataSharewithTime(DB, casenumber, time) // 根据选择的共享时长，判断是否能共享
	if !flag {
		return flag, "", ""
	}
	var user string
	SQLString := "select username from login where state ='1'"
	err := DB.QueryRow(SQLString).Scan(&user)
	if err == sql.ErrNoRows {
		return flag, "", ""
	}
	Enpath := GetEncfileDir2SQL(DB, user, casenumber, policy) // 加密文件路径
	sharecode := CreateShare(DB, casenumber)                  // 返回共享数据code
	fmt.Println(Enpath, sharecode)

	return true, Enpath, sharecode

}

// 删除共享数据
func Delete(DB *sql.DB, casenumber string) bool {
	os.Remove("./cpabe/dir_data/"+casenumber+".txt.cpabe")
	SQLString := "UPDATE base_info SET _Addition1='',_Addition2='' where _CaseNumber='" + casenumber + "'"
	_, err := DB.Exec(SQLString)
	if err != nil {
		return false
	}
	return true

}

// 判断是否能共享
func ConfirmShare(DB *sql.DB, casenumber string, code string) (bool, string, string) {
	var share_code string
	SQLString := "select _Addition2 from base_info where _CaseNumber='" + casenumber + "'"
	rows, err := DB.Query(SQLString)
	if err != nil {
		return false,"NULL",err.Error()
	}
	for rows.Next() {
		rows.Scan(&share_code)
		//fmt.Println(base_time)
	}
	if share_code == code {
		var sub string
		SQLString1 := "select _SubjectMark from base_info where _CaseNumber='" + casenumber + "'"
		rows1, _ := DB.Query(SQLString1)
		for rows1.Next() {
			rows1.Scan(&sub)
			//fmt.Println(base_time)
		}
		var datapath string
		SQLString2 := "select _Addition1 from base_info where _CaseNumber='" + casenumber + "'"
		rows2, _ := DB.Query(SQLString2)
		for rows2.Next() {
			rows2.Scan(&datapath)
			//fmt.Println(base_time)
		}
		return true,sub,datapath
	}else{
		return false,"NULL","共享密码不正确"
	}

}


//func main() {
//	DB := InitDB()
//	casenumber := "c0bf4b7f73821"
//	time := "0,0,1"
//	user := "赵七"
//	policy := GetPolicy(DB, user)
//	// sharecode := CreateShare(caseNumber)
//	_, Enpath, sharecode := DataEnShare(DB, casenumber, policy, time)
//	code := "123456"
//	fmt.Println(Enpath, sharecode)
//	path, _ := CertShare(casenumber, code)
//	fmt.Println(path)
//}
